#include <Bitmap.hpp>

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>

Bitmap::Bitmap(uint32_t width, uint32_t height)
    : mPixels(width * height), mWidth(width), mHeight(height) {}

void Bitmap::clearColor(const Pixel &color) {
  for (size_t i = 0; i < mPixels.size(); i++) {
    mPixels[i] = color;
  }
}

Bitmap::Pixel Bitmap::getPixel(uint32_t x, uint32_t y) {
  if (x >= mWidth || y >= mHeight) {
    return {};
  }

  return mPixels[pixelIndex(x, y)];
}

void Bitmap::setPixel(uint32_t x, uint32_t y, const Pixel &pixel) {
  if (x >= mWidth || y >= mHeight) {
    return;
  }

  mPixels[pixelIndex(x, y)] = pixel;
}

void Bitmap::setPixel(uint32_t x, uint32_t y, const glm::vec3 &pixel) {
  uint8_t r = static_cast<uint8_t>(255.999 * pixel.r);
  uint8_t g = static_cast<uint8_t>(255.999 * pixel.g);
  uint8_t b = static_cast<uint8_t>(255.999 * pixel.b);

  this->setPixel(x, y, Pixel{r, g, b});
}

void Bitmap::setPixel(uint32_t x, uint32_t y, glm::vec3 const &pixel,
                      int32_t samples_per_pixel) {
  float scale = 1.f / samples_per_pixel;

  float r = glm::sqrt(pixel.r * scale);
  float g = glm::sqrt(pixel.g * scale);
  float b = glm::sqrt(pixel.b * scale);

  this->setPixel(x, y,
                 Pixel{
                     uint8_t(256 * glm::clamp(r, 0.f, 0.999f)),
                     uint8_t(256 * glm::clamp(g, 0.f, 0.999f)),
                     uint8_t(256 * glm::clamp(b, 0.f, 0.999f)),
                 });
}

void Bitmap::writeToFile(const char *file_name) {
  stbi_write_png(file_name, mWidth, mHeight, 4, mPixels.data(), 0);
}

uint32_t Bitmap::pixelIndex(uint32_t x, uint32_t y) { return y * mWidth + x; }

std::unique_ptr<Bitmap> Bitmap::CreateBitmap(uint32_t width,
                                             float pixel_radio) {
  uint32_t height = static_cast<uint32_t>(width / pixel_radio);

  return std::make_unique<Bitmap>(width, height);
}
